Appearance
Ctrl+Space 被三个模块同时注册,静默覆盖
状态: 🟢 已修复
日期: 2026-07-10 严重程度: 🔴 P1(功能静默失效,用户无法通过快捷键访问功能) 影响范围: frontend/src/core/main.ts + scene.ts + outfit.ts发现方式: 代码搜索(手动审计) 修复方案: shortcut-registry.ts → ShortcutRegistry 类,注册时检查冲突,先注册的生效
问题描述
Ctrl+Space 在三个模块里被同时注册:
main.ts— 切换动作模式scene.ts— 切换相机模式outfit.ts— 切换服装预设
浏览器的事件系统:后注册的监听覆盖先注册的。所以最后一个注册的 Ctrl+Space 生效,前两个被静默覆盖。
用户按 Ctrl+Space,想切动作模式——切的是服装预设。用户按 Ctrl+Space,想切相机模式——切的是服装预设。
没有任何报错。没有任何提示。快捷键只是"不工作了"。
根因分析
快捷键注册是分散式的。每个模块自己 document.addEventListener('keydown', ...),没有人管别的模块用什么键。
冲突检测不存在。注册冲突从运行时提前到了设计时——但没人检查设计时。
为什么没有暴露
注册快捷键时,开发者只关心"我的快捷键能工作",不会去看别的模块有没有用同一个键。
这是协作型 bug——单独看每个模块的代码,都是正确的。只有把它们放在一起,才会发现冲突。
修复方案
src/core/shortcut-registry.ts:ShortcutRegistry 类。
注册时检查冲突:如果同一个 key 已经被注册,记录冲突来源并打印 console.warn。先注册的生效,后注册的被忽略。
注销需要同时匹配 key 和 source——防止误删其他模块注册的同一个键。
Ctrl+Space 的冲突被发现后,scene.ts 和 outfit.ts 的快捷键改为其他 key。
教训
- 协作型 bug 是最难发现的 — 单独看每个模块,都是正确的。冲突只存在于"放在一起"的时候
- 静默覆盖比报错更危险 — 报错至少告诉你哪里有问题。静默覆盖让你以为功能还在